
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Serialization;

using Derm.Render;

namespace ShaderGen
{
	/// <summary>
	/// A shader library project.
	/// </summary>
	/// <remarks>
	/// <para>
	/// 
	/// </para>
	/// </remarks>
	[XmlType("ShaderLibraryProject")]
	public class ShaderLibraryProject
	{
		/// <summary>
		/// Absolute path used for loading this ShaderLibraryProject.
		/// </summary>
		[XmlIgnore()]
		public string ProjectPath;

		/// <summary>
		/// Absolute path of the directory of this ShaderLibraryProject.
		/// </summary>
		public string ProjectDir { get { return (Path.GetDirectoryName(ProjectPath)); } }

		/// <summary>
		/// A collection of shader library paths used for composing this project. Paths are relative to <see cref="ProjectDir"/>.
		/// </summary>
		[XmlElement("IncludeLibrary")]
		public List<string> LibraryPaths
		{
			get { return (mLibrariesPath); }
			set { mLibrariesPath = (value != null) ? value : null; }
		}

		/// <summary>
		/// Load all shader libraries specified by <see cref="LibraryPaths"/>.
		/// </summary>
		/// <param name="log">
		/// A <see cref="System.String"/> that specify any error encountered during the library loading.
		/// </param>
		/// <remarks>
		/// This method do not throw any exception, but outputs a log about the library loading.
		/// </remarks>
		public void LoadLibraries(out string log)
		{
			log = null;

			if (mLibrariesPath != null) {
				StringBuilder sb = new StringBuilder();

				foreach (string shaderLibraryPath in mLibrariesPath) {
					try {
						string path = Path.IsPathRooted(shaderLibraryPath) ? shaderLibraryPath: Path.Combine(ProjectDir, shaderLibraryPath);

						mLibraries.Add(ShaderLibraryConfiguration.Load(path));
					} catch (Exception exception) {
						sb.AppendFormat("unable to load shader library from {0}: {1}\n", shaderLibraryPath, exception.Message);
					}
				}
				log = sb.Length == 0 ? null : sb.ToString();
			}
		}

		/// <summary>
		/// Add a library to this project.
		/// </summary>
		/// <param name="shaderLibrary"></param>
		public void AddLibrary(ShaderLibraryConfiguration shaderLibrary)
		{
			mLibraries.Add(shaderLibrary);
		}

		internal static string GetRelativePath(string fromPath, string toPath)
		{
			string fromFull = fromPath;
			string toFull = toPath;

			List<string> fromParts = new List<string>(fromFull.Split(Path.DirectorySeparatorChar));
			List<string> toParts = new List<string>(toFull.Split(Path.DirectorySeparatorChar));

			fromParts.RemoveAll(string.IsNullOrEmpty);
			toParts.RemoveAll(string.IsNullOrEmpty);

			// Remove all the same parts in front
			bool areRelative = false;

			while ((fromParts.Count > 0) && (toParts.Count > 0) && (StringComparer.OrdinalIgnoreCase.Compare(fromParts[0], toParts[0]) == 0)) {
				fromParts.RemoveAt(0);
				toParts.RemoveAt(0);
				areRelative = true;
			}

			if (areRelative == false)
				return (toPath);

			// Number of remaining fromParts is number of parent dirs
			StringBuilder ret = new StringBuilder();

			for (int i = 0; i < fromParts.Count; i++)
			{
				if (ret.Length > 0)
					ret.Append(Path.DirectorySeparatorChar);
				ret.Append("..");
			}

			// And the remainder of toParts
			foreach (string part in toParts) {
				if (ret.Length > 0)
					  ret.Append(Path.DirectorySeparatorChar);
				ret.Append(part);
			}

			return ret.ToString();
		}

		/// <summary>
		/// 
		/// </summary>
		public IEnumerable<ShaderLibraryConfiguration> Libraries { get { return (mLibraries); } }

		/// <summary>
		/// 
		/// </summary>
		private readonly List<ShaderLibraryConfiguration> mLibraries = new List<ShaderLibraryConfiguration>();

		/// <summary>
		/// Path cached during the loading process.
		/// </summary>
		private List<string> mLibrariesPath;

		/// <summary>
		/// Load a shader library project.
		/// </summary>
		/// <param name="path"></param>
		/// <returns></returns>
		public static ShaderLibraryProject Load(string path)
		{
			if (path == null)
				throw new ArgumentNullException("path");

			ShaderLibraryProject project;

			using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) {
				project = (ShaderLibraryProject)sXmlSerializer.Deserialize(fs);
			}
			project.ProjectPath = path;

			return (project);
		}

		/// <summary>
		/// Save this shader library project.
		/// </summary>
		public void Save() { Save(ProjectPath); }

		/// <summary>
		/// Save this shader library project.
		/// </summary>
		/// <param name="path"></param>
		/// <returns></returns>
		public void Save(string path)
		{
			if (path == null)
				throw new ArgumentNullException("path");

			using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write)) {
				sXmlSerializer.Serialize(fs, this);
			}
		}

		/// <summary>
		/// Serializer used for load/save ShaderLibraryProject.
		/// </summary>
		private static readonly XmlSerializer sXmlSerializer = new XmlSerializer(typeof(ShaderLibraryProject));
	}
}
